package org.andrewwinter.tescopricewar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class NameAndVolume
{
    private static final String NUMBER = "[0-9]+";
    private static final String OPTIONAL_PACK = "( *[Pp]ack)?";
    private static final String UNIT = "[Gg]|[lL]| ?[Mm]l|[Kk]g|[Cc]l|[Pp]k|'?S| [Pp]ack| [Ll]itre| [Ll]trs";
    private static final String MEASURE = "[0-9]+";
    private static final String BY = " *[Xx] *";
    private static final String OPTIONAL_TRAILING_DESCRIPTION = ".*$";

    private String unparsed;

    private String name;

    private Volume volume;

    public NameAndVolume(final String nameAndVolume)
    {
        unparsed = nameAndVolume;
    }

    public String getName()
    {
        if (name == null)
            parse();

        return name;
    }

    public Volume getVolume()
    {
        if (volume == null)
            parse();

        return volume;
    }


    private static final String group(int number, String regex)
    {
        return "(" + regex + ")";
    }

    private boolean parseFormat1(final String unparsed)
    {
        Pattern p = Pattern.compile(group(1,".*") + " " + group(2,MEASURE) + group(3,UNIT) + BY + group(4,NUMBER) + OPTIONAL_TRAILING_DESCRIPTION);

        final Matcher m = p.matcher(unparsed);

        if (m.matches())
        {
            name = m.group(1);
            final int weightOrMeasure = Integer.valueOf(m.group(2));
            final String unit = m.group(3);
            final int numberInPack = Integer.valueOf(m.group(4));

            volume = new Volume(weightOrMeasure, Unit.get(unit), numberInPack);
            return true;
        }

        return false;
    }

    private boolean parseFormat2(final String unparsed)
    {
        Pattern p = Pattern.compile(group(1,".*") + " " + group(2,NUMBER) + OPTIONAL_PACK + BY + group(4,MEASURE) + group(5,UNIT) + OPTIONAL_TRAILING_DESCRIPTION);

        final Matcher m = p.matcher(unparsed);

        if (m.matches())
        {
            name = m.group(1);
            final int weightOrMeasure = Integer.valueOf(m.group(4));
            final String unit = m.group(5);
            final int numberInPack = Integer.valueOf(m.group(2));

            volume = new Volume(weightOrMeasure, Unit.get(unit), numberInPack);

            return true;
        }

        return false;
    }

    private boolean parseFormat3(final String unparsed)
    {
        Pattern p = Pattern.compile(group(1,".*") + " " + group(2,MEASURE) + group(3,UNIT) + OPTIONAL_TRAILING_DESCRIPTION);

        final Matcher m = p.matcher(unparsed);

        if (m.matches())
        {
            name = m.group(1);
            final int weightOrMeasure = Integer.valueOf(m.group(2));
            final String unit = m.group(3);
            final int numberInPack = 1;

            volume = new Volume(weightOrMeasure, Unit.get(unit), numberInPack);

            return true;
        }

        return false;
    }



    private void parse()
    {
        if (parseFormat1(unparsed))
        {
            return;
        }
        else if (parseFormat2(unparsed))
        {
            return;
        }
        else if (parseFormat3(unparsed))
        {
            return;
        }
        else
        {
            if (unparsed.endsWith("x330"))
            {
                if (parseFormat2(unparsed + "ml"))
                {
                    return;
                }
            }

            name = unparsed;
            volume = new Volume(1, Unit.DEFAULT, 1);
        }
    }


    @Override
    public String toString()
    {
        return getName() + " " + getVolume();
    }
}
